package org.osgroup.project;

import org.osgroup.visitor.BaseVisitor;

import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 餐馆类 - 订单驱动的生产者-消费者模型
 */
public class Restaurant {
    
    /** 订单类 */
    public static class Order {
        private final String orderId;
        private final String itemId;
        private final String itemName;
        private final BaseVisitor customer;
        private final String restaurantId;
        private final double price;
        private final long preparationTime;
        private final long orderTime;
        private OrderStatus status;
        
        public enum OrderStatus { PENDING, COOKING, COMPLETED }
        
        public Order(String orderId, String itemId, String itemName, BaseVisitor customer,
                     String restaurantId, double price, long preparationTime) {
            this.orderId = orderId;
            this.itemId = itemId;
            this.itemName = itemName;
            this.customer = customer;
            this.restaurantId = restaurantId;
            this.price = price;
            this.preparationTime = preparationTime;
            this.orderTime = System.currentTimeMillis();
            this.status = OrderStatus.PENDING;
        }
        
        public String getOrderId() { return orderId; }
        public String getItemId() { return itemId; }
        public String getItemName() { return itemName; }
        public BaseVisitor getCustomer() { return customer; }
        public String getRestaurantId() { return restaurantId; }
        public double getPrice() { return price; }
        public long getPreparationTime() { return preparationTime; }
        public long getOrderTime() { return orderTime; }
        public OrderStatus getStatus() { return status; }
        public void setStatus(OrderStatus status) { this.status = status; }
        
        @Override
        public String toString() {
            return String.format("Order{id='%s', item='%s', status=%s}", orderId, itemName, status);
        }
    }
    
    /** 菜单项类 */
    public static class MenuItem {
        private final String itemId;
        private final String itemName;
        private final double price;
        private final long preparationTime;
        private final String description;
        private boolean available;
        
        public MenuItem(String itemId, String itemName, double price, long preparationTime, String description) {
            this.itemId = itemId;
            this.itemName = itemName;
            this.price = price;
            this.preparationTime = preparationTime;
            this.description = description;
            this.available = true;
        }
        
        public MenuItem(String itemId, String itemName, double price, long preparationTime) {
            this(itemId, itemName, price, preparationTime, "");
        }
        
        public String getItemId() { return itemId; }
        public String getItemName() { return itemName; }
        public double getPrice() { return price; }
        public long getPreparationTime() { return preparationTime; }
        public String getDescription() { return description; }
        public boolean isAvailable() { return available; }
        public void setAvailable(boolean available) { this.available = available; }
        
        @Override
        public String toString() {
            return String.format("MenuItem{%s, ¥%.2f}", itemName, price);
        }
    }
    
    /** 餐品类 */
    public static class Meal {
        private final String mealName;
        private final String mealId;
        private final String restaurantId;
        private final double price;
        private final long preparationTime;
        private BaseVisitor customer;
        private final long createdTime;
        
        public Meal(String mealId, String mealName, String restaurantId, double price, long preparationTime) {
            this.mealId = mealId;
            this.mealName = mealName;
            this.restaurantId = restaurantId;
            this.price = price;
            this.preparationTime = preparationTime;
            this.createdTime = System.currentTimeMillis();
        }
        
        public String getMealName() { return mealName; }
        public String getMealId() { return mealId; }
        public String getRestaurantId() { return restaurantId; }
        public double getPrice() { return price; }
        public long getPreparationTime() { return preparationTime; }
        public BaseVisitor getCustomer() { return customer; }
        public void setCustomer(BaseVisitor customer) { this.customer = customer; }
        public long getCreatedTime() { return createdTime; }
        
        @Override
        public String toString() {
            return String.format("Meal{%s, ¥%.2f}", mealName, price);
        }
    }
    
    // 餐馆基本属性
    private final String restaurantName;
    private final String restaurantId;
    private final Map<String, MenuItem> menu;
    private final List<MenuItem> menuList;
    private final Lock menuLock;
    
    // 厨师线程管理
    private final List<Thread> chefThreads;
    private int chefCount;
    
    // 订单队列
    private final int ORDER_QUEUE_CAPACITY;
    private final Queue<Order> orderQueue;
    
    // 餐品缓冲区
    private final int BUFFER_CAPACITY;
    private final Queue<Meal> mealBuffer;
    
    // 同步控制
    private final Lock lock;
    private final Condition orderNotFull;
    private final Condition orderNotEmpty;
    private final Condition notFull;
    private final Condition notEmpty;
    
    /** 累计订单总数 */
    private int totalOrders;
    
    /** 累计出餐总数 */
    private int totalMealsProduced;
    
    /** 累计取餐总数 */
    private int totalMealsConsumed;
    
    /** 等待下单的游客数量 */
    private int waitingOrderCount;
    
    /** 等待接单的厨师数量 */
    private int waitingChefCount;
    
    /** 等待取餐的游客数量 */
    private int waitingVisitorCount;
    
    /** 餐馆是否营业 */
    private boolean isOpen;
    
    /** 订单ID计数器 */
    private int orderIdCounter;
    
    /**
     * 构造一个餐馆对象
     * 
     * @param restaurantId 餐馆ID
     * @param restaurantName 餐馆名称
     * @param capacity 餐品缓冲区容量
     */
    public Restaurant(String restaurantId, String restaurantName, int capacity) {
        this(restaurantId, restaurantName, capacity, capacity * 2); // 订单队列容量默认为缓冲区的2倍
    }
    
    /**
     * 构造一个餐馆对象(指定订单队列容量)
     * 
     * @param restaurantId 餐馆ID
     * @param restaurantName 餐馆名称
     * @param capacity 餐品缓冲区容量
     * @param orderQueueCapacity 订单队列容量
     */
    public Restaurant(String restaurantId, String restaurantName, int capacity, int orderQueueCapacity) {
        this.restaurantId = restaurantId;
        this.restaurantName = restaurantName;
        this.BUFFER_CAPACITY = capacity;
        this.ORDER_QUEUE_CAPACITY = orderQueueCapacity;
        this.menu = new HashMap<>();
        this.menuList = new ArrayList<>();
        this.menuLock = new ReentrantLock(true);
        this.orderQueue = new LinkedList<>();
        this.mealBuffer = new LinkedList<>();
        this.lock = new ReentrantLock(true); // 公平锁
        this.orderNotFull = lock.newCondition();
        this.orderNotEmpty = lock.newCondition();
        this.notFull = lock.newCondition();
        this.notEmpty = lock.newCondition();
        this.totalOrders = 0;
        this.totalMealsProduced = 0;
        this.totalMealsConsumed = 0;
        this.waitingOrderCount = 0;
        this.waitingChefCount = 0;
        this.waitingVisitorCount = 0;
        this.isOpen = true; // 默认营业
        this.orderIdCounter = 0;
        this.chefThreads = new ArrayList<>();
        this.chefCount = 0;
    }
    
    
    // ==================== 订单管理 ====================
    
    /** 
     * 游客下订单(阻塞式)
     * 订单队列满时会阻塞等待，直到有空位或餐馆打烊
     */
    public Order placeOrder(BaseVisitor visitor, String itemId) throws InterruptedException {
        lock.lock();
        try {
            if (!isOpen) return null;
            
            MenuItem menuItem = getMenuItem(itemId);
            if (menuItem == null || !menuItem.isAvailable()) return null;
            
            while (orderQueue.size() >= ORDER_QUEUE_CAPACITY) {
                waitingOrderCount++;
                orderNotFull.await();
                waitingOrderCount--;
                if (!isOpen) return null;
            }
            
            String orderId = restaurantId + "-O" + String.format("%04d", ++orderIdCounter);
            Order order = new Order(orderId, itemId, menuItem.getItemName(), visitor,
                                    restaurantId, menuItem.getPrice(), menuItem.getPreparationTime());
            orderQueue.offer(order);
            totalOrders++;
            
            System.out.println("📝 " + visitor.getName() + " 订购 " + menuItem.getItemName());
            orderNotEmpty.signalAll();
            return order;
        } finally {
            lock.unlock();
        }
    }
    
    /** 
     * 游客尝试下订单(非阻塞式)
     * 订单队列满时立即返回null，不会等待
     */
    public Order tryPlaceOrder(BaseVisitor visitor, String itemId) {
        lock.lock();
        try {
            if (!isOpen || orderQueue.size() >= ORDER_QUEUE_CAPACITY) return null;
            
            MenuItem menuItem = getMenuItem(itemId);
            if (menuItem == null || !menuItem.isAvailable()) return null;
            
            String orderId = restaurantId + "-O" + String.format("%04d", ++orderIdCounter);
            Order order = new Order(orderId, itemId, menuItem.getItemName(), visitor,
                                    restaurantId, menuItem.getPrice(), menuItem.getPreparationTime());
            orderQueue.offer(order);
            totalOrders++;
            
            orderNotEmpty.signalAll();
            return order;
        } finally {
            lock.unlock();
        }
    }
    
    /** 
     * 厨师接单并制作(阻塞式)
     * 从订单队列取订单 → 制作(模拟耗时) → 放入餐品缓冲区
     * 没有订单时会阻塞等待
     */
    public Meal cookFromOrder() throws InterruptedException {
        Order order;
        lock.lock();
        try {
            while (orderQueue.isEmpty()) {
                if (!isOpen) return null;
                waitingChefCount++;
                orderNotEmpty.await();
                waitingChefCount--;
            }
            
            order = orderQueue.poll();
            if (order == null) return null;
            
            order.setStatus(Order.OrderStatus.COOKING);
            orderNotFull.signalAll();
        } finally {
            lock.unlock();
        }
        
        // 制作(锁外)
        Thread.sleep(order.getPreparationTime());
        
        // 放入缓冲区
        lock.lock();
        try {
            while (mealBuffer.size() >= BUFFER_CAPACITY) {
                notFull.await();
            }
            
            String mealId = order.getOrderId().replace("-O", "-M");
            Meal meal = new Meal(mealId, order.getItemName(), restaurantId, 
                                order.getPrice(), order.getPreparationTime());
            meal.setCustomer(order.getCustomer());
            
            mealBuffer.offer(meal);
            totalMealsProduced++;
            order.setStatus(Order.OrderStatus.COMPLETED);
            
            System.out.println("✅ 制作完成: " + order.getItemName());
            notEmpty.signalAll();
            return meal;
        } finally {
            lock.unlock();
        }
    }
    
    /** 
     * 游客取餐(阻塞式)
     * 从缓冲区取餐，缓冲区为空时会阻塞等待
     */
    public Meal consumeMeal(BaseVisitor visitor) throws InterruptedException {
        lock.lock();
        try {
            while (isEmpty()) {
                if (!isOpen) return null;
                waitingVisitorCount++;
                notEmpty.await();
                waitingVisitorCount--;
            }
            
            Meal meal = mealBuffer.poll();
            if (meal != null) {
                meal.setCustomer(visitor);
                totalMealsConsumed++;
                System.out.println("🍽️  " + visitor.getName() + " 取到 " + meal.getMealName());
                notFull.signalAll();
            }
            return meal;
        } finally {
            lock.unlock();
        }
    }
    
    /** 
     * 尝试出餐(非阻塞式)
     * 缓冲区满时立即返回false，不会等待
     */
    public boolean tryProduceMeal(Meal meal) {
        lock.lock();
        try {
            if (!isOpen || isFull()) return false;
            mealBuffer.offer(meal);
            totalMealsProduced++;
            notEmpty.signalAll();
            return true;
        } finally {
            lock.unlock();
        }
    }
    
    /** 
     * 尝试取餐(非阻塞式)
     * 缓冲区为空时立即返回null，不会等待
     */
    public Meal tryConsumeMeal(BaseVisitor visitor) {
        lock.lock();
        try {
            if (!isOpen || isEmpty()) return null;
            
            Meal meal = mealBuffer.poll();
            if (meal != null) {
                meal.setCustomer(visitor);
                totalMealsConsumed++;
                notFull.signalAll();
            }
            return meal;
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 判断缓冲区是否已满
     */
    public boolean isFull() {
        return mealBuffer.size() >= BUFFER_CAPACITY;
    }
    
    /**
     * 判断缓冲区是否为空
     */
    public boolean isEmpty() {
        return mealBuffer.isEmpty();
    }
    
    /**
     * 获取当前缓冲区大小
     */
    public int getCurrentSize() {
        return mealBuffer.size();
    }
    
    /**
     * 获取缓冲区容量
     */
    public int getCapacity() {
        return BUFFER_CAPACITY;
    }
    
    /**
     * 开始营业
     * 默认启动2个厨师线程
     */
    public void open() {
        open(2);
    }
    
    /**
     * 开始营业并指定厨师数量
     * @param numberOfChefs 厨师数量
     */
    public void open(int numberOfChefs) {
        lock.lock();
        try {
            if (isOpen && !chefThreads.isEmpty()) {
                System.out.println("⚠️  [" + restaurantName + " 已经在营业中]");
                return;
            }
            
            isOpen = true;
            System.out.println("🏪 [" + restaurantName + " 开始营业]");
            
            // 启动厨师线程
            for (int i = 0; i < numberOfChefs; i++) {
                startChef("厨师-" + (++chefCount));
            }
            
            orderNotEmpty.signalAll();
            orderNotFull.signalAll();
            notEmpty.signalAll();
            notFull.signalAll();
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 启动一个厨师线程
     * @param chefName 厨师名称
     */
    private void startChef(String chefName) {
        Thread chefThread = new Thread(() -> {
            try {
                System.out.println("👨‍🍳 [" + restaurantName + "] " + chefName + " 开始工作");
                while (!Thread.interrupted() && isOpen) {
                    cookFromOrder();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("👋 [" + restaurantName + "] " + chefName + " 下班了");
        });
        
        chefThread.setDaemon(true);
        chefThread.setName(restaurantName + "-" + chefName);
        chefThread.start();
        chefThreads.add(chefThread);
    }
    
    /**
     * 打烊
     */
    public void close() {
        lock.lock();
        try {
            isOpen = false;
            System.out.println("🔒 [" + restaurantName + " 已打烊]");
            orderNotEmpty.signalAll(); // 唤醒所有等待接单的厨师
            orderNotFull.signalAll();  // 唤醒所有等待下单的游客
            notEmpty.signalAll();      // 唤醒所有等待取餐的游客
            notFull.signalAll();       // 唤醒所有等待出餐的厨师
        } finally {
            lock.unlock();
        }
        
        // 停止所有厨师线程
        for (Thread chef : chefThreads) {
            chef.interrupt();
        }
        
        // 等待厨师线程结束
        for (Thread chef : chefThreads) {
            try {
                chef.join(1000); // 最多等待1秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        chefThreads.clear();
        System.out.println("👋 [" + restaurantName + " 所有厨师已下班]");
    }
    
    /**
     * 清空缓冲区
     */
    public void clearBuffer() {
        lock.lock();
        try {
            int clearedMeals = mealBuffer.size();
            int clearedOrders = orderQueue.size();
            mealBuffer.clear();
            orderQueue.clear();
            System.out.println("🧹 [" + restaurantName + " 清空缓冲区] 已清除 " + clearedMeals + " 份餐品, " + clearedOrders + " 个订单");
            notFull.signalAll();
            orderNotFull.signalAll();
        } finally {
            lock.unlock();
        }
    }
    
    // ==================== 菜单管理 ====================
    
    /** 添加菜单项 */
    public boolean addMenuItem(MenuItem menuItem) {
        menuLock.lock();
        try {
            if (menu.containsKey(menuItem.getItemId())) return false;
            menu.put(menuItem.getItemId(), menuItem);
            menuList.add(menuItem);
            System.out.println("✅ 添加菜品: " + menuItem.getItemName());
            return true;
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 移除菜单项 */
    public boolean removeMenuItem(String itemId) {
        menuLock.lock();
        try {
            MenuItem item = menu.remove(itemId);
            if (item == null) return false;
            menuList.remove(item);
            return true;
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 根据ID获取菜单项 */
    public MenuItem getMenuItem(String itemId) {
        menuLock.lock();
        try {
            return menu.get(itemId);
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 获取所有菜单项 */
    public List<MenuItem> getMenuItems() {
        menuLock.lock();
        try {
            return new ArrayList<>(menuList);
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 获取所有可售菜品 */
    public List<MenuItem> getAvailableMenuItems() {
        menuLock.lock();
        try {
            List<MenuItem> available = new ArrayList<>();
            for (MenuItem item : menuList) {
                if (item.isAvailable()) available.add(item);
            }
            return available;
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 设置菜品是否可售 */
    public boolean setMenuItemAvailable(String itemId, boolean available) {
        menuLock.lock();
        try {
            MenuItem item = menu.get(itemId);
            if (item == null) return false;
            item.setAvailable(available);
            return true;
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 根据菜单项创建餐品 */
    public Meal createMealFromMenu(String itemId, String mealId) {
        menuLock.lock();
        try {
            MenuItem item = menu.get(itemId);
            if (item == null || !item.isAvailable()) return null;
            return new Meal(mealId, item.getItemName(), restaurantId, 
                            item.getPrice(), item.getPreparationTime());
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 从菜单随机创建一份餐品 */
    public Meal createRandomMeal(String mealId) {
        List<MenuItem> available = getAvailableMenuItems();
        if (available.isEmpty()) return null;
        
        Random random = new Random();
        MenuItem item = available.get(random.nextInt(available.size()));
        return new Meal(mealId, item.getItemName(), restaurantId, 
                        item.getPrice(), item.getPreparationTime());
    }
    
    /** 打印餐馆菜单 */
    public void printMenu() {
        menuLock.lock();
        try {
            System.out.println("\n【" + restaurantName + " - 菜单】");
            if (menuList.isEmpty()) {
                System.out.println("  (暂无菜品)");
            } else {
                for (int i = 0; i < menuList.size(); i++) {
                    MenuItem item = menuList.get(i);
                    String status = item.isAvailable() ? "✅" : "❌";
                    System.out.printf("  %d. %s %s - ¥%.2f%n", 
                                      i + 1, status, item.getItemName(), item.getPrice());
                }
            }
            System.out.println();
        } finally {
            menuLock.unlock();
        }
    }
    
    /** 获取餐馆状态信息 */
    public String getStatus() {
        lock.lock();
        try {
            return String.format(
                "\n===== %s =====\n" +
                "营业: %s | 订单: %d/%d | 缓冲: %d/%d\n" +
                "累计: 订单%d 出餐%d 取餐%d\n" +
                "等待: 下单%d 接单%d 取餐%d\n",
                restaurantName,
                isOpen ? "✅" : "🔒",
                orderQueue.size(), ORDER_QUEUE_CAPACITY,
                getCurrentSize(), BUFFER_CAPACITY,
                totalOrders, totalMealsProduced, totalMealsConsumed,
                waitingOrderCount, waitingChefCount, waitingVisitorCount
            );
        } finally {
            lock.unlock();
        }
    }
    
    // ==================== Getter 方法 ====================
    
    /** 获取餐馆名称 */
    public String getRestaurantName() { return restaurantName; }
    
    /** 获取餐馆ID */
    public String getRestaurantId() { return restaurantId; }
    
    /** 判断餐馆是否营业 */
    public boolean isOpen() {
        lock.lock();
        try {
            return isOpen;
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取累计出餐总数 */
    public int getTotalMealsProduced() { return totalMealsProduced; }
    
    /** 获取累计取餐总数 */
    public int getTotalMealsConsumed() { return totalMealsConsumed; }
    
    /** 获取累计订单总数 */
    public int getTotalOrders() { return totalOrders; }
    
    /** 获取当前订单队列大小 */
    public int getOrderQueueSize() {
        lock.lock();
        try {
            return orderQueue.size();
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取订单队列容量 */
    public int getOrderQueueCapacity() { return ORDER_QUEUE_CAPACITY; }
    
    /** 获取等待接单的厨师数量 */
    public int getWaitingChefCount() {
        lock.lock();
        try {
            return waitingChefCount;
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取等待取餐的游客数量 */
    public int getWaitingVisitorCount() {
        lock.lock();
        try {
            return waitingVisitorCount;
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取等待下单的游客数量 */
    public int getWaitingOrderCount() {
        lock.lock();
        try {
            return waitingOrderCount;
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取订单队列的快照(用于API接口) */
    public List<Order> getOrderQueueSnapshot() {
        lock.lock();
        try {
            return new ArrayList<>(orderQueue);
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取餐品缓冲区的快照(用于API接口) */
    public List<Meal> getMealBufferSnapshot() {
        lock.lock();
        try {
            return new ArrayList<>(mealBuffer);
        } finally {
            lock.unlock();
        }
    }
    
    /** 获取当前厨师数量 */
    public int getChefCount() {
        return chefThreads.size();
    }
    
    /** 动态添加厨师 */
    public void addChef() {
        lock.lock();
        try {
            if (!isOpen) {
                System.out.println("⚠️  [" + restaurantName + " 未营业,无法添加厨师]");
                return;
            }
            startChef("厨师-" + (++chefCount));
        } finally {
            lock.unlock();
        }
    }
    
    /** 动态添加多个厨师 */
    public void addChefs(int count) {
        lock.lock();
        try {
            if (!isOpen) {
                System.out.println("⚠️  [" + restaurantName + " 未营业,无法添加厨师]");
                return;
            }
            for (int i = 0; i < count; i++) {
                startChef("厨师-" + (++chefCount));
            }
        } finally {
            lock.unlock();
        }
    }
}
